package top.malaoshi.webmvc.factory;

import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.jdbc.core.JdbcTemplate;
import top.malaoshi.easy.logging.Log;
import top.malaoshi.easy.logging.LogFactory;
import top.malaoshi.sprout.jdbc.Transaction;
import top.malaoshi.webmvc.exception.BeanCreationException;
import top.malaoshi.webmvc.info.ClassInfo;
import top.malaoshi.webmvc.utils.FieldUtil;

import java.lang.reflect.Method;

public class ProxyObjectFactory implements ObjectFactory{
    private ClassInfo classInfo;

    public ProxyObjectFactory(ClassInfo classInfo) {
        this.classInfo = classInfo;
    }

    @Override
    public Object getObject() throws BeanCreationException {
        // 不能使用 clazz.newInstance() 创建对象，遇到 private 私有构造方法会报错无权限
        try {
            Class clazz = classInfo.getClazz();

            Enhancer e = new Enhancer();
            e.setSuperclass(clazz);
            e.setCallbackFilter(new CallbackFilter());

            // 定义回调，顺序与 CallbackFilter 返回值一致，所以不要改变
            Callback[] callbacks = {
                    new DefaultMethodInterceptorImpl(),
                    new TransactionalMethodInterceptorImpl()
            };

            e.setCallbacks(callbacks);
            return e.create();
        } catch (Exception e) {
            throw new BeanCreationException(e);
        }
    }
    class CallbackFilter implements net.sf.cglib.proxy.CallbackFilter{

        @Override
        public int accept(Method method) {
            // 如果该类的方法 有 @transactional 注解，返回1
            boolean isMethodTr = method.isAnnotationPresent(Transactional.class);
            if(isMethodTr){
                return 1;
            }else{
                // 如果类上有 @transactional 注解，返回1
                boolean isClassTr = method.getDeclaringClass().isAnnotationPresent(Transactional.class);
                if( isClassTr ){
                    return 1;
                }
            }

            return 0;
        }
    }
    /**
     * 默认方法拦截器，直接调用目标对象方法
      */
    class DefaultMethodInterceptorImpl implements MethodInterceptor {

        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            //调用目标类对象的方法
            return methodProxy.invokeSuper(o, args);
        }
    }
    /**
     * 处理由 @Transactional 事务注解修饰的方法
     */
    class TransactionalMethodInterceptorImpl implements MethodInterceptor {

        final Log LOG = LogFactory.getLog(ProxyObjectFactory.class);
        @Override
        public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            // 方法执行结果
            Object res = null;
            /*
            如果 类和当前方法都有 @Transactional 注解修饰，以该方法上的 @Transactional 注解优先级为最高
             */
            Transactional transactional = null;
            {
                transactional = method.getAnnotation(Transactional.class);
                if (transactional == null) {
                    transactional = o.getClass().getAnnotation(Transactional.class);
                }
            }
            LOG.debug("由 @Transactional 进行事务管理");

//             获取该类中所有 JdbcTemplate 对象
            JdbcTemplate[] jdbcTemplates = FieldUtil.getFieldsByType(method.getDeclaringClass(),o, JdbcTemplate.class);
            // 让所有 JdbcTemplate 对象设置为不自动提交
            for(JdbcTemplate item : jdbcTemplates){
                item.setAutoCommit(false);
                Transaction transaction = item.getTransaction();
                transaction.setReadOnly(transactional.readOnly());
                transaction.setIsolation(transactional.isolation().value());

            }

            // 如果抛异常，该异常与注解的rollbackFor是否匹配
            boolean matchRollbackFor = false;
            // 如果抛异常，该异常与注解的 matchNoRollbackFor
            boolean matchNoRollbackFor = false;
            boolean isThrowException = false;
            try {
                //调用目标类对象的方法
                res = methodProxy.invokeSuper(o, args);
                // 如果成功运行，不是只读事务，就让所有 JdbcTemplate 对象提交
                if(!transactional.readOnly()) {
                    for (JdbcTemplate item : jdbcTemplates) {
                        item.commmit();
                    }
                }
            }catch (Throwable e){
                isThrowException = true;
                // 不是只读事务，就继续处理
                if( !transactional.readOnly() ) {
                    // 判断抛出异常，与 noRollbackFor 类型是否匹配
                    for (Class<? extends Throwable> item : transactional.noRollbackFor()) {
                        if (item.isInstance(e)) {
                            matchNoRollbackFor = true;
                        }
                    }
                    if (!matchNoRollbackFor) {
                        // 判断抛出异常，与 rollbackFor 类型是否匹配
                        for (Class<? extends Throwable> item : transactional.rollbackFor()) {
                            if (item.isInstance(e)) {
                                matchRollbackFor = true;
                            }
                        }
                        // 如果异常匹配，就回滚
                        if (matchRollbackFor) {
                            for (JdbcTemplate item : jdbcTemplates) {
                                LOG.debug("事务回滚");
                                item.rollback();
                            }
                        }
                    }
                }
                throw e;
            }finally {
                // 不是只读事务，就继续处理
                if( !transactional.readOnly() ) {
                    // 抛出异常时，如果该异常与 noRollbackFor 匹配就提交，或者异常与 rollbackFor 不匹配，就提交
                    if (isThrowException && (!matchRollbackFor || matchNoRollbackFor)) {
                        for (JdbcTemplate item : jdbcTemplates) {
                            LOG.debug("没有匹配的异常");
                            item.commmit();
                        }
                    }
                }
                for(JdbcTemplate item : jdbcTemplates){
                    item.close();
                }

            }

            return res;
        }
    }
}
